#include "vsf.h"
#include "core.h"

#define VSFHAL_USART_NUM			3

struct w600_uart_reg_t
{
	uint32_t LC;
	uint32_t FC;
	uint32_t DMAC;
	uint32_t FIFOC;
	uint32_t BD;
	uint32_t INTM;
	uint32_t INTS;
	uint32_t FIFOS;
	uint32_t TXW;
	uint32_t RESERVED[3];
	uint32_t RXW;
};

#define W600_REGCFG_UART(BASE, IRQN)		\
	{.reg = (struct w600_uart_reg_t *)(BASE), .irqn = (IRQN),}
struct w600_uart_t
{
	volatile struct w600_uart_reg_t *reg;
	IRQn_Type irqn;

	struct
	{
		void *param;
		void (*ontx)(void *);
		void (*onrx)(void *);
	} cb;
} vsfhal_uart[VSFHAL_USART_NUM] =
{
	W600_REGCFG_UART(HR_UART0_BASE_ADDR, UART0_IRQn),
	W600_REGCFG_UART(HR_UART1_BASE_ADDR, UART1_IRQn),
	W600_REGCFG_UART(HR_UART2_BASE_ADDR, UART2_IRQn),
};

// 		index : 0
//		bit0 - bit1		: usart index		2bit
//		RX : 2
//		bit2 - bit2		: port				1bit
//		bit3 - bit7		: pin				5bit
//		bit8 - bit10	: remap				3bit
//		bit11 - bit11	: pull				1bit
//		TX : 12
//		bit12 - bit12	: port				1bit
//		bit13 - bit17	: pin				5bit
//		bit18 - bit20	: remap				3bit
//		bit21 - bit21	: pull				1bit
//		nCTS: 22
//		bit22 - bit22	: port				1bit
//		bit23 - bit27	: pin				5bit
//		bit28 - bit30	: remap				3bit
//		bit31 - bit31	: pull				1bit
//		nRTS: 32
//		bit32 - bit32	: port				1bit
//		bit33 - bit37	: pin				5bit
//		bit38 - bit40	: remap				3bit
//		bit41 - bit41	: pull				1bit

#define VSFHAL_UART_PINNUM					4
#define VSFHAL_USART_IDX					2
#define VSFHAL_USART_IO						10
#define VSFHAL_USART_IO_PORT				1
#define VSFHAL_USART_IO_PIN					5
#define VSFHAL_USART_IO_REMAP				3
#define VSFHAL_USART_IO_PULL				1

#define VSFHAL_USART_IDX_MASK				((1 << VSFHAL_USART_IDX) - 1)
#define VSFHAL_USART_IO_MASK				((1 << VSFHAL_USART_IO) - 1)
#define VSFHAL_USART_IO_PORT_OFFSET			(0)
#define VSFHAL_USART_IO_PORT_MASK			(((1 << VSFHAL_USART_IO_PORT) - 1) << VSFHAL_USART_IO_PORT_OFFSET)
#define VSFHAL_USART_IO_PIN_OFFSET			(VSFHAL_USART_IO_PORT_OFFSET + VSFHAL_USART_IO_PORT)
#define VSFHAL_USART_IO_PIN_MASK			(((1 << VSFHAL_USART_IO_PIN) - 1) << VSFHAL_USART_IO_PIN_OFFSET)
#define VSFHAL_USART_IO_REMAP_OFFSET		(VSFHAL_USART_IO_PIN_OFFSET + VSFHAL_USART_IO_PIN)
#define VSFHAL_USART_IO_REMAP_MASK			(((1 << VSFHAL_USART_IO_REMAP) - 1) << VSFHAL_USART_IO_REMAP_OFFSET)
#define VSFHAL_USART_IO_PULL_OFFSET			(VSFHAL_USART_IO_REMAP_OFFSET + VSFHAL_USART_IO_REMAP)
#define VSFHAL_USART_IO_PULL_MASK			(((1 << VSFHAL_USART_IO_PULL) - 1) << VSFHAL_USART_IO_PULL_OFFSET)

vsf_err_t vsfhal_usart_init(vsfhal_usart_t index)
{
	uint8_t uart_idx = (uint8_t)(index & VSFHAL_USART_IDX_MASK);
	struct w600_uart_t *uart = &vsfhal_uart[uart_idx];
	volatile struct w600_uart_reg_t *reg = uart->reg;
	uint8_t i, port, pin, pull;
	uint16_t remap;
	vsf_err_t err;

	if (uart_idx >= dimof(vsfhal_uart))
		return VSFERR_NOT_SUPPORT;

	memset(&uart->cb, 0, sizeof(uart->cb));
	reg->LC = 0;
	reg->FC = 0;		// no flowcontrol by default

	index >>= VSFHAL_USART_IDX;
	for(i = 0; i < VSFHAL_UART_PINNUM; i++)
	{
		remap = index & VSFHAL_USART_IO_MASK;
		index >>= VSFHAL_USART_IO;
		if (remap)
		{
			port = (remap & VSFHAL_USART_IO_PORT_MASK) >> VSFHAL_USART_IO_PORT_OFFSET;
			pin = (remap & VSFHAL_USART_IO_PIN_MASK) >> VSFHAL_USART_IO_PIN_OFFSET;
			pull = (remap & VSFHAL_USART_IO_PULL_MASK) >> VSFHAL_USART_IO_PULL_OFFSET;
			remap = (remap & VSFHAL_USART_IO_REMAP_MASK) >> VSFHAL_USART_IO_REMAP_OFFSET;
			err = vsfhal_gpio_afsel(port, pin, remap, pull);
			if (err != VSFERR_NONE) return err;

			if (i > 1)
				reg->FC = UFC_ENABLE | UFC_RTS_TRIGGER_LVL_28_BYTE;
		}
	}
	return VSFERR_NONE;
}

vsf_err_t vsfhal_usart_fini(vsfhal_usart_t index)
{
	uint8_t uart_idx = (uint8_t)(index & VSFHAL_USART_IDX_MASK);
	volatile struct w600_uart_reg_t *reg = vsfhal_uart[uart_idx].reg;
	if (uart_idx >= dimof(vsfhal_uart)) return VSFERR_NOT_SUPPORT;

	reg->LC = 0;
	reg->INTM = 0xFFFFFFFF;
	return VSFERR_NONE;
}

vsf_err_t vsfhal_usart_config(vsfhal_usart_t index, uint32_t baudrate, uint32_t mode)
{
	uint8_t uart_idx = (uint8_t)(index & VSFHAL_USART_IDX_MASK);
	struct w600_uart_t *uart = &vsfhal_uart[uart_idx];
	volatile struct w600_uart_reg_t *reg = uart->reg;
	if (uart_idx >= dimof(vsfhal_uart)) return VSFERR_NOT_SUPPORT;

	struct vsfhal_info_t *info;
	if (vsfhal_core_get_info(&info))
		return VSFERR_FAIL;

	uint32_t apbclk = info->apb_freq_hz;
	reg->BD = (apbclk / (16 * baudrate) - 1) |
			(((apbclk % (baudrate * 16)) * 16 / (baudrate * 16)) << 16);
	reg->LC = ULCON_TX_EN | ULCON_WL8 | (mode & 0x1C);
	reg->INTS = 0xFFFFFFFF;
	reg->INTM = ~(UIS_RX_FIFO | UIS_TX_FIFO | UIS_RX_FIFO_TIMEOUT | UIS_TX_FIFO_EMPTY);
	reg->DMAC = (4UL << UDMA_RX_FIFO_TIMEOUT_SHIFT) | UDMA_RX_FIFO_TIMEOUT;
	reg->FIFOC = UFC_TX_FIFO_LVL_16_BYTE | UFC_RX_FIFO_LVL_16_BYTE |
			UFC_TX_FIFO_RESET | UFC_RX_FIFO_RESET;

	vsfhal_usart_rx_enable(uart_idx);
	NVIC_EnableIRQ(uart->irqn);
	return VSFERR_NONE;
}

vsf_err_t vsfhal_usart_config_cb(vsfhal_usart_t index, uint32_t int_priority,
				void *p, void (*ontx)(void *), void (*onrx)(void *))
{
	uint8_t uart_idx = (uint8_t)(index & VSFHAL_USART_IDX_MASK);
	struct w600_uart_t *uart = &vsfhal_uart[uart_idx];
	if (uart_idx >= dimof(vsfhal_uart)) return VSFERR_NOT_SUPPORT;

	uart->cb.param = p;
	uart->cb.ontx = ontx;
	uart->cb.onrx = onrx;
	return VSFERR_NONE;
}

uint16_t vsfhal_usart_tx_bytes(vsfhal_usart_t index, uint8_t *data, uint16_t size)
{
	volatile struct w600_uart_reg_t *reg =
			vsfhal_uart[index & VSFHAL_USART_IDX_MASK].reg;
	uint16_t i;

	for (i = 0; i < size; i++)
		reg->TXW = data[i];
	return size;
}

uint16_t vsfhal_usart_tx_get_data_size(vsfhal_usart_t index)
{
	volatile struct w600_uart_reg_t *reg =
			vsfhal_uart[index & VSFHAL_USART_IDX_MASK].reg;
	return reg->FIFOS & UFS_TX_FIFO_CNT_MASK;
}

uint16_t vsfhal_usart_tx_get_free_size(vsfhal_usart_t index)
{
	return 16 - vsfhal_usart_tx_get_data_size(index);
}

uint16_t vsfhal_usart_rx_bytes(vsfhal_usart_t index, uint8_t *data, uint16_t size)
{
	volatile struct w600_uart_reg_t *reg =
			vsfhal_uart[index & VSFHAL_USART_IDX_MASK].reg;
	uint16_t i;

	for (i = 0; i < size; i++)
	{
		if ((reg->FIFOS >> 6) & UFS_TX_FIFO_CNT_MASK)
			data[i] = reg->RXW;
		else
			break;
	}
	return i;
}

uint16_t vsfhal_usart_rx_get_data_size(vsfhal_usart_t index)
{
	volatile struct w600_uart_reg_t *reg =
			vsfhal_uart[index & VSFHAL_USART_IDX_MASK].reg;
	return (reg->FIFOS >> 6) & UFS_TX_FIFO_CNT_MASK;
}

uint16_t vsfhal_usart_rx_get_free_size(vsfhal_usart_t index)
{
	uint32_t fifo_len = 16;
	return fifo_len - vsfhal_usart_rx_get_data_size(index);
}

vsf_err_t vsfhal_usart_rx_enable(vsfhal_usart_t index)
{
	uint8_t uart_idx = (uint8_t)(index & VSFHAL_USART_IDX_MASK);
	volatile struct w600_uart_reg_t *reg = vsfhal_uart[uart_idx].reg;

	reg->LC |= ULCON_RX_EN;
	return VSFERR_NONE;
}

vsf_err_t vsfhal_usart_rx_disable(vsfhal_usart_t index)
{
	uint8_t uart_idx = (uint8_t)(index & VSFHAL_USART_IDX_MASK);
	volatile struct w600_uart_reg_t *reg = vsfhal_uart[uart_idx].reg;

	reg->LC &= ~ULCON_RX_EN;
	return VSFERR_NONE;
}

static void uart_handler(uint8_t index)
{
	struct w600_uart_t *uart = &vsfhal_uart[index];
	volatile struct w600_uart_reg_t *reg = uart->reg;
	uint32_t ints = reg->INTS & ~reg->INTM;

	reg->INTS = ints;
	if (ints & (UIS_RX_FIFO | UIS_RX_FIFO_TIMEOUT))
		uart->cb.onrx(uart->cb.param);
	if (ints & (UIS_TX_FIFO | UIS_TX_FIFO_EMPTY))
		uart->cb.ontx(uart->cb.param);
}

ROOTFUNC void UART0_IRQHandler(void)
{
	uart_handler(0);
}
ROOTFUNC void UART1_IRQHandler(void)
{
	uart_handler(1);
}
ROOTFUNC void UART2_IRQHandler(void)
{
	uart_handler(2);
}
